package com.typhoon.spring_shiro.utils.redis;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.util.CollectionUtils;

import com.typhoon.spring_shiro.utils.CommonUtils;
import com.typhoon.spring_shiro.utils.context.SpringContextUtil;

/**
 * Created by hujun on 2017/3/16.
 */
public class RedisUtils {

    private static StringRedisTemplate stringRedisTemplate = (StringRedisTemplate)SpringContextUtil.getBean("stringRedisTemplate");

    private static RedisTemplate<String, Object> redisTemplate = (RedisTemplate<String,Object>)SpringContextUtil.getBean("redisTemplate");

    private static final Logger LOG = LoggerFactory.getLogger(RedisUtils.class);

    /**
     * 删除缓存<br>
     * 根据key精确匹配删除
     * @param key
     */
    @SuppressWarnings("unchecked")
    public static void delObject(String... key){
        try {
            if (key != null && key.length > 0) {
                if (key.length == 1) {
                    redisTemplate.delete(key[0]);
                } else {
                    redisTemplate.delete(CollectionUtils.arrayToList(key));
                }
            }
        }catch (Exception e){
            LOG.error("delObject error|msg=" + e.getMessage());
        }
    }

    /**
     * 模糊删除<br>
     * （该操作会执行模糊查询，请尽量不要使用，以免影响性能或误删）
     * @param pattern
     */
    public static void dimDel(String pattern){
        if(pattern == null){
            return ;
        }
        try {
            redisTemplate.delete(redisTemplate.keys("*" + pattern + "*"));
        }catch (Exception e){
            LOG.error("dimDel error|msg=" + e.getMessage());
        }

    }

    /**
     * 取得缓存（int型）
     * @param key
     * @return
     */
    public static Integer getIntOfStr(String key){
        try {
            String value = stringRedisTemplate.boundValueOps(key).get();
            if (StringUtils.isNotBlank(value)) {
                return Integer.valueOf(value);
            }
        }catch (Exception e){
            LOG.error("getIntOfStr error|msg=" + e.getMessage());
        }
        return null;
    }

    /**
     * 取得缓存（字符串类型）
     * @param key
     * @return
     */
    public static String getStrOfStr(String key){
        try {
            return stringRedisTemplate.boundValueOps(key).get();
        }catch (Exception e){
            LOG.error("getStrOfStr error|msg=" + e.getMessage());
        }
        return null;
    }


    /**
     * 获取缓存<br>
     * 注：基本数据类型(Character除外)，请直接使用get(String key, Class<T> clazz)取值
     * @param key
     * @return
     */
    public static Object getObj(String key){
        try {
            return redisTemplate.boundValueOps(key).get();
        }catch (Exception e){
            LOG.error("getObj error|msg=" + e.getMessage());
        }
        return null;
    }


    /**
     * 获取缓存<br>
     * 注：该方法暂不支持Character数据类型
     * @param key   key
     * @param clazz 类型
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T get(String key, Class<T> clazz) {
        try {
            return (T) redisTemplate.boundValueOps(key).get();
        }catch (Exception e){
            LOG.error("get key=" + key + "|msg=" + e.getMessage());
        }
        return null;
    }



    /**
     * 将value对象写入缓存
     * @param key
     * @param value
     * @param time 失效时间(秒)
     */
    public static void setObject(String key,Object value,long time){
        try {
            redisTemplate.opsForValue().set(key, value);
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
        }catch (Exception e){
            LOG.error("setObject error|key=" + key,e);
        }
    }

    /**
     * 将value以String类型写入redis
     * @param key
     * @param value
     * @param time
     */
    public static void setString(String key,String value,long time){
        try {
            stringRedisTemplate.opsForValue().set(key, value);
            if (time > 0) {
                stringRedisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
        }catch (Exception e){
            LOG.error("setString error|key=" + key,e);
        }
    }


    /**
     * 递减操作
     * @param key
     * @param by
     * @return
     */
    public static double incr(String key, double by){
        try {
            if (!redisTemplate.hasKey(key)) {
                setObject(key, by, -1);
                return by;
            } else {
                double old = CommonUtils.evalDouble(getObj(key));
                setObject(key, old + by, -1);
                return old + by;
            }
        }catch (Exception e){
            LOG.error("incr error|key=" + key,e);
        }
        return 0.0;
    }


    /**
     * 获取double类型值
     * @param key
     * @return
     */
    public static double getDoubleOfStr(String key) {
        try {
            String value = stringRedisTemplate.boundValueOps(key).get();
            if (StringUtils.isNotBlank(value)) {
                return Double.valueOf(value);
            }
            return 0d;
        }catch (Exception e){
            LOG.error("getDoubleOfStr error|key=" + key,e);
        }
        return 0d;
    }

    /**
     * 设置double类型值
     * @param key
     * @param value
     * @param time 失效时间(秒)
     */
    public static void setDoubleOfStr(String key, double value, long time) {
        try {
            stringRedisTemplate.opsForValue().set(key, String.valueOf(value));
            if (time > 0) {
                stringRedisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
        }catch (Exception e){
            LOG.error("setDoubleOfStr error|key=" + key,e);
        }
    }

    /**
     * 设置double类型值
     * @param key
     * @param value
     * @param time 失效时间(秒)
     */
    public static void setIntOfStr(String key, int value, long time) {
        try {
            stringRedisTemplate.opsForValue().set(key, String.valueOf(value));
            if (time > 0) {
                stringRedisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
        }catch (Exception e){
            LOG.error("setIntOfStr error|key=" + key,e);
        }
    }

    /**
     * 将map写入缓存
     * @param key
     * @param map
     * @param time 失效时间(秒)
     */
    public static <T> void setMap(String key, Map<String, T> map, long time){
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
        }catch (Exception e){
            LOG.error("setMap error|key=" + key,e);
        }
    }


    /**
     * 向key对应的map中添加缓存对象
     * @param key   cache对象key
     * @param field map对应的key
     * @param value     值
     */
    public static void addField2Map(String key, String field, Object value){
        try {
            redisTemplate.opsForHash().put(key, field, value);
        }catch (Exception e){
            LOG.error("addField2Map error|key=" + key,e);
        }
    }


    /**
     * 获取map缓存
     * @param key
     * @param clazz
     * @return
     */
    public static <T> Map<String, T> mget(String key, Class<T> clazz){
        try {
            BoundHashOperations<String, String, T> boundHashOperations = redisTemplate.boundHashOps(key);
            return boundHashOperations.entries();
        }catch (Exception e){
            LOG.error("mget error|key=" + key,e);
        }
        return null;
    }


    /**
     * 获取map缓存中的某个对象
     * @param key
     * @param field
     * @param clazz
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getMapField(String key, String field, Class<T> clazz){
        try {
            return (T) redisTemplate.boundHashOps(key).get(field);
        }catch (Exception e){
            LOG.error("getMapField error|key=" + key,e);
        }
        return null;
    }

    /**
     * 删除map中的某个对象
     * @author lh
     * @date 2016年8月10日
     * @param key   map对应的key
     * @param field map中该对象的key
     */
    public void delMapField(String key, String... field){
        try {
            BoundHashOperations<String, String, ?> boundHashOperations = redisTemplate.boundHashOps(key);
            boundHashOperations.delete(field);
        }catch (Exception e){
            LOG.error("delMapField error|key=" + key);
        }
    }

    /**
     * hashKey 加减（只能是Number类型）
     * @param key
     * @param hashKey
     */
    public static void increaseHash(String key,String hashKey,long count){
        try {
            if (!hasKey(key)) {
                redisTemplate.opsForHash().put(key, hashKey, count);
            } else {
                long oldValue = CommonUtils.evalLong(redisTemplate.opsForHash().get(key, hashKey), 0);
                redisTemplate.opsForHash().put(key, hashKey, count + oldValue);
            }
        }catch (Exception e){
            LOG.error("increaseHash error|key=" + key + "|hashKey=" + hashKey);
        }
    }
    //---------------------------------------------------------hash 接口 end----------------------------------------

    /**
     * 指定缓存的失效时间
     *
     * @author FangJun
     * @date 2016年8月14日
     * @param key 缓存KEY
     * @param time 失效时间(秒)
     */
    public static void expire(String key, long time) {
        if(time > 0){
            redisTemplate.expire(key, time, TimeUnit.SECONDS);
        }
    }

    //-----------------------------------------------------set 操作==============================================
    /**
     * 添加set
     * @param key
     * @param value
     */
    public static void sadd(String key, String... value) {
        try {
            redisTemplate.boundSetOps(key).add(value);
        }catch (Exception e){
            LOG.error("sadd error|key=" + key ,e);
        }
    }

    /**
     * 删除set集合中的对象
     * @param key
     * @param value
     */
    public static void srem(String key, String... value) {
        try {
            redisTemplate.boundSetOps(key).remove(value);
        }catch (Exception e){
            LOG.error("srem error|key=" + key,e);
        }
    }

    /**
     * set重命名
     * @param oldkey
     * @param newkey
     */
    public static void srename(String oldkey, String newkey){
        try {
            redisTemplate.boundSetOps(oldkey).rename(newkey);
        }catch (Exception e){
            LOG.error("srename error|",e);
        }
    }

    /**
     * 获取集合中数量
     * @param key
     * @return
     */
    public static long scard(String key){
        try{
           return redisTemplate.boundSetOps(key).size();
        }catch (Exception e){
            LOG.error("scard error|key=" + key,e);
        }
        return 0;
    }

    /**
     * 获取set中所有成员
     * @param key
     * @return
     */
    public static Set<Object> smembers(String key){
        try {
            return redisTemplate.boundSetOps(key).members();
        }catch (Exception e){
            LOG.error("getMembers error|key=" + key,e);
        }
        return new HashSet<>();
    }

    /**
     * 移除并返回集合中的一个随机元素。
     * @param key
     * @return
     */
    public static Object spop(String key){
        try{
            return redisTemplate.boundSetOps(key).pop();
        }catch (Exception e){
            LOG.error("spop error|key=" + key,e);
        }
        return null;
    }

    //-----------------------------------------------------set 操作 end==============================================

    //=========================================zset 开始=====================================

    /**
     * 往zset中添加元素
     * @param key
     * @param value
     * @param score
     */
    public static void zadd(String key,Object value,Double score){
        try {
            redisTemplate.opsForZSet().add(key, value, score);
        }catch (Exception e){
            LOG.error("zadd error|key=" + key);
        }
    }

    /**
     * 添加所有set元素
     * @param key
     * @param values
     */
    public static void zaddAll(String key,Set<ZSetOperations.TypedTuple<Object>> values){
        try{
            redisTemplate.opsForZSet().add(key,values);
        }catch (java.lang.Exception e){
            LOG.error("zaddAll error|key=" + key,e);
        }
    }

    /**
     * 返回有序集 key 中，指定区间内的成员。按照score值从小到大排序
     * @param key
     * @param clazz
     * @param start
     * @param end
     * @param <T>
     * @return
     */
    public static <T> Set<T> zrange(String key,Class<T> clazz,int start,int end){
        try {
            return (Set<T>) redisTemplate.opsForZSet().range(key, start, end);
        }catch (Exception e){
            LOG.error("zrange error|key=" + key,e);
        }
        return null;
    }

    /**
     *返回有序集 key 中，指定区间内的成员。
     * 其中成员的位置按 score 值递减(从大到小)来排列。
     * @param key
     * @param clazz
     * @param start
     * @param end
     * @param <T>
     * @return
     */
    public static <T> Set<T> zRevRange(String key,Class<T> clazz,int start,int end){
        try {
            return (Set<T>) redisTemplate.opsForZSet().reverseRange(key, start, end);
        }catch (Exception e){
            LOG.error("zRevRange error|key=" + key,e);
        }
        return null;
    }

    /**
     * 返回有序集 key 中，所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列。
     * @param key
     * @param clazz
     * @param min
     * @param max
     * @param <T>
     * @return
     */
    public static <T> Set<T> zrangeByScore(String key,Class<T> clazz,double min,double max) {
        try {
            return (Set<T>) redisTemplate.opsForZSet().rangeByScore(key,min,max);
        }catch (Exception e){
            LOG.error("zRevRange error|key=" + key,e);
        }
        return null;
    }

    /**
     * 返回有序集 key 中，所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递减(从大到小)次序排列。
     * @param key
     * @param clazz
     * @param min
     * @param max
     * @param <T>
     * @return
     */
    public static <T> Set<T> zRevRangeByScore(String key,Class<T> clazz,double min,double max){
        try {
            return (Set<T>) redisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
        }catch (Exception e){
            LOG.error("zRevRangeByScore error|key=" + key,e);
        }
        return null;
    }

    /**
     * 移除有序集 key 中的一个或多个成员，不存在的成员将被忽略。
     * @param key
     * @param value
     */
    public static void zrem(String key,Object... value){
        try{
            redisTemplate.opsForZSet().remove(key,value);
        }catch (Exception e){
            LOG.error("zrem erro|key=" + key,e);
        }
    }

    /**
     * 通过score来删除元素（包括等于 min 或 max ）
     * @param key
     * @param minScore
     * @param maxScore
     */
    public static void zremByScore(String key,double minScore,double maxScore){
        try {
            redisTemplate.opsForZSet().removeRangeByScore(key, minScore, maxScore);
        }catch (Exception e){
            LOG.error("zremByScore error|key=" + key,e);
        }
    }

    /**
     * 通过下标来删除元素
     * @param key
     * @param start
     * @param end
     */
    public static void zremRange(String key,long start,long end){
        try{
            redisTemplate.opsForZSet().removeRange(key,start,end);
        }catch (Exception e){
            LOG.error("zremRange error|key=" + key,e);
        }
    }
    //-----------------------------------------------zset结束----------------------------------------

    /**
     * 模糊查询keys
     * @param pattern
     * @return
     */
    public static Set<String> keys(String pattern){
        return redisTemplate.keys(pattern);
    }

    /**
     * 删除key
     * @param key
     */
    public static void delKey(String key){
        try {
            redisTemplate.delete(key);
        }catch (Exception e){
            LOG.error("delKey error|key=" + key);
        }
    }

    /**
     * 是否存在key
     * @param key
     * @return
     */
    public static boolean hasKey(String key){
        try{
           return redisTemplate.hasKey(key);
        }catch (Exception e){
            LOG.error("hasKey error|key=" + key,e);
        }
        return false;
    }
}
